package org.example.demo3.demos.web.VmsTransactionFlowInvoice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.example.demo3.demos.web.VmsTransactionFlowInvoice.domain.*;
import org.example.demo3.demos.web.VmsTransactionFlowInvoice.mapper.VmsTransactionFlowInvoiceMapper;
import org.example.demo3.demos.web.VmsTransactionFlowInvoice.service.VmsTransactionFlowInvoiceService;
import org.example.demo3.demos.web.common.ResponseEnum;
import org.example.demo3.demos.web.common.Result;
import org.example.demo3.demos.web.constants.CommonConstant;
import org.example.demo3.demos.web.entity.*;
import org.example.demo3.demos.web.entity.vmsfulloutinvoice.VmsFullOutInvoiceDetails;
import org.example.demo3.demos.web.entity.vmsfulloutinvoice.VmsFullOutInvoiceItems;
import org.example.demo3.demos.web.entity.vmsfulloutinvoice.VmsFullOutInvoiceItemsExp;
import org.example.demo3.demos.web.enums.CommonEnum;
import org.example.demo3.demos.web.enums.FpztEnum;
import org.example.demo3.demos.web.enums.NegeClEnum;
import org.example.demo3.demos.web.exception.BusinessException;
import org.example.demo3.demos.web.model.dto.vmsfulloutinvoice.VmsFullOutInvoiceInfoDto;
import org.example.demo3.demos.web.model.dto.vmsmanualinvoice.VmsManualBaseDto;
import org.example.demo3.demos.web.model.dto.vmsmanualinvoice.VmsManualDetailDto;
import org.example.demo3.demos.web.model.dto.vmsmanualinvoice.VmsManualExtendDto;
import org.example.demo3.demos.web.model.dto.vmstransactionflowinvoice.VmsTransactionFlowInvoiceDto;
import org.example.demo3.demos.web.model.negativestream.vmsmanualinvoice.VmsManualBaseVo;
import org.example.demo3.demos.web.model.negativestream.vmsmanualinvoice.VmsManualDetailVo;
import org.example.demo3.demos.web.model.negativestream.vmsmanualinvoice.VmsManualExtendVo;
import org.example.demo3.demos.web.model.negativestream.vmstransactionflowinvoice.VmsManualInvoiceVo;
import org.example.demo3.demos.web.model.negativestream.vmstransactionflowinvoice.VmsTransacitonFlowInvoiceItemVo;
import org.example.demo3.demos.web.negativestream.NvatNegativestreamForVatService;
import org.example.demo3.demos.web.redis.RedisService;
import org.example.demo3.demos.web.utils.BeanUtils;
import org.example.demo3.demos.web.utils.DateUtils;
import org.example.demo3.demos.web.vmsEnterpriseInfo.VmsEnterpriseInfoService;
import org.example.demo3.demos.web.vmsepsInformation.VmsEpsInformationService;
import org.example.demo3.demos.web.vmsnvattrandtadvaltax.Service.NvatTrandtadvalextXinhuaService;
import org.example.demo3.demos.web.vmsnvattrandtadvaltax.Service.VmsNvatRevenuemapselfService;
import org.example.demo3.demos.web.vmsnvattrandtadvaltax.domain.NvatTrandtadvalextXinhua;
//import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class VmsTransactionFlowInvoiceServiceImpl extends ServiceImpl<VmsTransactionFlowInvoiceMapper, NvatTrandtadvaltaxsep>  implements VmsTransactionFlowInvoiceService {
    @Autowired
    VmsTransactionFlowInvoiceMapper vmsTransactionFlowInvoiceMapper;
    @Autowired
    NvatTrandtadvalextXinhuaService nvatTrandtadvalextXinhuaService;
    @Autowired
    VmsNvatRevenuemapselfService nvatRevenuemapselfService;
    @Autowired
    NvatNegativestreamForVatService nvatNegativestreamForVatService;
    @Autowired
    RedisService redisService;
    @Autowired
    VmsEnterpriseInfoService vmsEnterpriseInfoService;
    @Autowired
    VmsEpsInformationService vmsEpsInformationService;
    private static final String  LOG_MSG = "发票匹配流水的日志";
    private static final String  username = "唐开放-测试私有名字";

    private String deptCode = "1010";
    @Override
    public Result<IPage<VmsTransactionFlowInvoicePageVo>> getTransactionFlowInvoiceList(VmsTransactionFlowInvoicePageDto dto, Page<VmsTransactionFlowInvoicePageVo> page) {
        Page<NvatTrandtadvaltaxsep> page1 = new Page<>(page.getCurrent(), page.getSize());
        IPage<VmsTransactionFlowInvoicePageVo> transactionFlowInvoiceList1 = vmsTransactionFlowInvoiceMapper.getTransactionFlowInvoiceList(page1,deptCode,dto); // mp的分页传递给mapper的时候,要是第一个.
        return  Result.OK(transactionFlowInvoiceList1);
    }

    @Override
    public Result<List<VmsMergeBo>> preMerge(preMergeDTO dto) {
        List<VmsMergeBo> list  =  mergeRes(dto);


        return Result.OK(list);
    }

    // 还原确认按钮
    @Transactional
    @Override
    public Result<Object> restoreFlow(String id) {

        // 根据id,获取交易里的xrdid的  关系表,yrdid和
        NvatRevenuemapself entity = nvatRevenuemapselfService.getRevenuemapselftByXrdid(id);
//        146607208  - 这个是发票流水列表， 因为去 self 拆分表里没查到， 所以说明没拆分过。就要报错。

        if(ObjectUtil.isNull(entity)){ // 如果没有这个 新的Rdid,就说明没数据.
            throw  new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_DOES_NOT_EXIST);
        }
        // 2. 校验
        checkFlowInvoiceStatus(entity);


        // 3. 判断是拆分还是合并, 拆分和合并走不通的逻辑
        if(CommonEnum.SPLIT_MERGE_TYPE_1.getKey().equals(entity.getCzlx())){// 拆分
            // 还原拆分
//            这里还有很多行 要处理
            restoreSplit(id);
        }else{ // 合并
            // 还原合并
            restoreMerge(id);
        }
        return Result.OK("还原成功");
    }

    private  void restoreMerge(String id) {
        // 1. 获取合并信息 (需要被删除的数据)
        VmsFlowInvoiceDetailVo mergeInfo = this.baseMapper.getMergeInfo(id); // 这个里面包含了主表, 拓展表, 部门信息
        // 2.获取明细数据
        List<VmsFlowInvoiceDetailVo> mergeItemList = baseMapper.getMergeItemList(id); // 找到yrdid 是一样的

        // 3.还原被合并的数据
        // - 返回一个List，里面是明细的列表信息。 List<VmsFlowInvoiceDetailVo>
        List<NvatTrandtadvaltaxsep> collect = mergeItemList.stream().map(vmsFlowInvoiceDetailVo -> {
            NvatTrandtadvaltaxsep taxsep = new NvatTrandtadvaltaxsep();
            taxsep.setRdid(vmsFlowInvoiceDetailVo.getRdid());
            taxsep.setCfhbzt(Long.valueOf(CommonEnum.SPLIT_MERGE_STATUS_1.getKey())); // 拆分合并状态：1未拆分合并，2拆分，3合并
            return taxsep;
        }).collect(Collectors.toList());
        boolean b = updateBatchById(collect); // 批量修改,并且全都完成就会成功
        if(!b){
            throw  new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED);
        }



        // 4. 删除合并出来的内条数据
        boolean b1 = remove(new LambdaQueryWrapper<NvatTrandtadvaltaxsep>()
                .eq(NvatTrandtadvaltaxsep::getRdid, mergeInfo.getRdid())
        );
        if(!b1){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED);
        }

        // 5. 删除流水扩展信息表信息
        boolean b2 = nvatTrandtadvalextXinhuaService.remove(new LambdaQueryWrapper<NvatTrandtadvalextXinhua>().eq(NvatTrandtadvalextXinhua::getRdid, id));
        if(!b2){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED); // 转换失败,都是同一个报错.
        }

        // 6. 删除合并关联表
        // - 删除关系表 ， NvatRevenuemapself
        // ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED
        boolean b3 = nvatRevenuemapselfService.remove(new LambdaQueryWrapper<NvatRevenuemapself>().eq(NvatRevenuemapself::getXrdid, id));
        if(!b3){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED); // 回退失败
        }


        // 7. 负数流水
        ArrayList<Long> list = new ArrayList<>();
        // 7.1 先遍历,准备需要修改的数据
        mergeItemList.forEach(item->{
            if(item.getJyje().compareTo(BigDecimal.ZERO) == -1){
                list.add(item.getRdid());
            }
        });

        // 7.2 如果 list 不等于空 就修改流水状态为 未处理状态
        if(list.size()>0){
            LambdaUpdateWrapper<NvatNegativestream> in = new LambdaUpdateWrapper<NvatNegativestream>()
                    .set(NvatNegativestream::getClzt, CommonEnum.NEGATIVE_FLOW_STATUS_0.getKey()) //处理状态，0为未处理，1为已处理,入库时默认写入0
                    .set(NvatNegativestream::getCzr, "") // 操作人
                    .set(NvatNegativestream::getCzrq, "") // 操作日期
                    .set(NvatNegativestream::getCllx, "") // 处理类型 ， 1.犹豫期退费，2.定期结算，9.其它 可以默认为空
                    .set(NvatNegativestream::getCzsm, "") // 处理操作填写的操作说明
                    .in(NvatNegativestream::getRdid, list);// 这些全都需要更新
            boolean update = nvatNegativestreamForVatService.update(in);
            if(!update){
                throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED);
            }
        }

        // 7.3 删除被合并出来的流水信息 先判断是否为负数 是的话就删除掉(负数流水部分删除）
        // - BigDecimal有compareTo，  左侧比右侧小，就是-1 ，相同就是 0 ，大于就是1
        if(mergeInfo.getJyje().compareTo(BigDecimal.ZERO) == -1){
            boolean remove = nvatNegativestreamForVatService.remove(new LambdaQueryWrapper<NvatNegativestream>().eq(NvatNegativestream::getRdid, mergeInfo.getRdid()));
            if(!remove){
                throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_RECOVERY_FAILED);
            }
        }

    }

    public void checkFlowInvoiceStatus(NvatRevenuemapself entity){
        Long xrdid = entity.getXrdid();
        String czlx = entity.getCzlx();
        long count = 0;
        if(CommonEnum.SPLIT_MERGE_TYPE_1.getKey().equals(czlx)){
           count =   baseMapper.verifySplit(xrdid);
        }else if(CommonEnum.SPLIT_MERGE_STATUS_2.getKey().equals(czlx)){
            count = baseMapper.verifyMerge(xrdid);
        }
        if(count> 0 ){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_HAS_BEEN_INVOICED);
        }
    }
    public Result<Object> checkFlowInvoiceStatus(String id){
        NvatRevenuemapself revenuemapselftByXrdid = nvatRevenuemapselfService.getRevenuemapselftByXrdid(id);
        checkFlowInvoiceStatus(revenuemapselftByXrdid);
        return Result.OK("只要是通过了,就是通了");
    }

    private void restoreSplit(String id) {
        /*
        * 1. 拆分根据id, 从self里找到xrdid 是id的, 摘出 yrdid
        * 2.  把原有sep里的拆分状态 改成1
        * 3.然后找到多条yrdid 一样的多条.
        * 4. 把他们都删除,
        * */
        //1. 拆分根据id, 从self里找到xrdid 是id的, 摘出 yrdid
        VmsFlowInvoiceDetailVo splitInfo = baseMapper.getSplitInfo(id);
        //  2.把原有sep里的拆分状态 改成1
        boolean update = update(new LambdaUpdateWrapper<NvatTrandtadvaltaxsep>()
                .eq(NvatTrandtadvaltaxsep::getRdid, splitInfo.getRdid()) // 这个地方的id 一直用的传入的,那个值是没有的. 需要用查询来的数据.
                .set(NvatTrandtadvaltaxsep::getCfhbzt, "1"));
//                .set(NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_1.getKey()));
        if(!update){
            throw new BusinessException(ResponseEnum.MERGING_ERROR);
        }

//        3.然后找到多条yrdid 一样的多条.
//        Stream<Long> delIds =
        List<Long> delIds = baseMapper.getSplitItemList(id).stream().map(item -> item.getRdid()).collect(Collectors.toList());
//        4. 把他们都删除,
        // sep删除
        boolean b = removeByIds(delIds);
        if(!b){
            throw new BusinessException(ResponseEnum.DB_UPDATE_FAILED);
        }
        // xinhua删除
        boolean b1 = nvatTrandtadvalextXinhuaService.removeByIds(delIds);
        if(!b1){
            throw new BusinessException(ResponseEnum.DB_UPDATE_FAILED);
        }
        // self删除
        boolean b2 = nvatRevenuemapselfService.remove(
                new LambdaQueryWrapper<NvatRevenuemapself>()
                        .eq(NvatRevenuemapself::getYrdid,splitInfo.getRdid())
        );
        System.out.println(b2);
        if(!b2){
            throw new BusinessException(ResponseEnum.DB_UPDATE_FAILED);
        }


//        throw new BusinessException("999","分拆逻辑写完了");
    }

    public List<VmsMergeBo> mergeRes(preMergeDTO dto){
//        总结功能: 把传入的对象,根据 合并条件查询当前部门/选中的rdid,返回列表.
//                    看是根据税率还是根据险种代码合并,将业务编号,税率,不含税金额,等参数都进行修改并且返回.

        // 1.验证数据 并且返回编号
        String bh = getBh(dto);
        String ywbh = null;
        String tbdh = null;
        if(CommonEnum.MERGE_QUERY_CONDITION_0.getKey().equals(dto.getMergeQueryCondition())){
            ywbh = bh;
        }else{
            tbdh = bh;
        }
        // 2.查询数据
        List<NvatTrandtadvaltaxsep> list = null;
        // 2.1按照投保单号或者业务编号获取 发票匹配流水的列表, 根据部门,根据rdid,根据流水状态
        if(dto.getAllMerge() == true){ // 全部就是符合业务编号/投保单号,并且符合部门的 全部数据都查出来.
           list =   this.baseMapper.mergeQuery(ywbh,tbdh,deptCode,null);
        }else{ // 非全部,就会劈开rdids,在sql语句最终使用in,只查询选中的值.
            // - 这里是根据 逗号， 劈开，不然就没有数据。 实际是列表里需要多选，不多选就没有数据。
            // 2.2 按照业务编号或者投保单号获取指定也就是选择的多个数据
            if(StringUtils.isNotEmpty(dto.getRdids())){
                String[] rdids = dto.getRdids().split(",");
                List<String> list1 = Arrays.asList(rdids);
                list =  this.baseMapper.mergeQuery(ywbh,tbdh,deptCode,list1);
            }

        }
        // 3.合并数据
        HashMap<String,VmsMergeBo> map = new HashMap<>();
        if(CollectionUtils.isNotEmpty(list)){
            for (NvatTrandtadvaltaxsep ntp : list) {
                // 比如 0.06 就会都是按照key放在 一起
//                {
//                    "0.06": new VmsMergeBo(),
//                    "0.06 + xzdm = 实际:0.060000199000"
//                }
                String key = ntp.getSysl().toString(); // 数组循环，摘出税率。（bigDecimal改成字符串）
                if(CommonEnum.MERGE_MODE_0.getKey().equals(dto.getMergeMode())){
                    // 如果按照险种分类
                    key += ntp.getExpcol3(); // 增加险种代码到key上
                }
                // 这里只要是重复的对象,就会一直用重复的 - 因为里面的值 需要一直累加.
                // 要是不重复的,就先给一个空的对象.
                VmsMergeBo bo =    map.computeIfAbsent(key,k->new VmsMergeBo());  // 没有值，就设置一个空对象。  有值不处理。

                addVmsMergeBo(dto,ntp,bo,bh);
            }
        }
        System.out.println(map);

        //        BeanUtils.copyProperties(list,res);
        // 4.合并后数据 - 重新计算并且转换
        List<VmsMergeBo> values = new ArrayList<>();
        if(map.size()>0){
            Collection<VmsMergeBo> values1 = map.values();
            values = values1.stream().map(e -> {
                //计算税额 税额=不含税金额*税率
                BigDecimal se = e.getBhsje().multiply(e.getSysl());
                //计算的税额和实际金额的差值
                BigDecimal ce =  e.getXxse().subtract(se);


                // 写一个大数计算的1
                BigDecimal one = new BigDecimal(CommonConstant.STR_1);
                //如果差值大于0.06, 差额的绝对值,如果大于0 ,
                if (ce.abs().compareTo(new BigDecimal(CommonConstant.STR_006)) > 0) {
                    //不含税金额=含税金额/1+税率   设置不含税金额,使用交易金额 / 1(外层的1)+税率 (第一个是数字,第二个是保留几位,第三个是四舍五入的模式)
                    BigDecimal bhsje = e.getJyje().divide(one.add(e.getSysl()), 2, RoundingMode.HALF_UP);
                    e.setBhsje(bhsje);
                    //税额=含税金额-不含税金额 交易金额.subtract(不含税金额)
                    BigDecimal se1 = e.getJyje().subtract(e.getBhsje());
                    e.setXxse(se1);
                }
                return e;
            }).collect(Collectors.toList());
        }


        return  values;
    }

    private VmsMergeBo addVmsMergeBo(preMergeDTO dto, NvatTrandtadvaltaxsep ntp, VmsMergeBo bo, String bh) {

        // 如果传入的对象 有值(不为空)，并且rdids是空的（是重置rdids）
        // rdids 是null/ 是空集合,就会进入.
        if(bo !=null && CollectionUtil.isEmpty(bo.getRdids())){ // 这里用的是bo,就是原本对象里,是否有rdids, 或者说是最终返回的值,是否有.
            //初始根据合并模式 险种代码和险种名称 简称进行赋值
            if(CommonEnum.MERGE_MODE_0.getKey().equals(dto.getMergeMode())){
                // 外层选择的根据险种代码， 那这里就设置这些参数

                // 设置险种代码,从ntp里设置第三个
                bo.setXzdm(ntp.getExpcol3());
                // 设置险种简称,设置为第四个expco
                bo.setXzjc(ntp.getExpcol4());
                // 设置险种全程, 是ytbs1
                bo.setXzqc(ntp.getYtbs1()); // 这里是借用字段,使用的xml里的 as功能. 到这里再转换去.

            }else if(CommonEnum.MERGE_MODE_1.getKey().equals(dto.getMergeMode())){ // 税率
                // 这种就是按照税率设置， 清空险种代码

                // 设置险种代码为空
                bo.setXzdm("");
                // 设置险种简称为保费,从公共常量里设置
                bo.setXzjc(CommonConstant.BF);
                // 设置险种全程为保费,从公共常量里设置
                bo.setXzqc(CommonConstant.BF);
            }

            //证件类型号码 手机号 电子邮箱 纳税人识别号等进行赋值
            // 设置是否首期业务,为空.
            bo.setIssqyw("");
            // 设置是否个人业务为空
            bo.setIsgryw("");
            // 设置手机号, 借用 表中的字段 ytbs2
            bo.setSjh(ntp.getYtbs2()); // 使用xml应该重命名方式
            // 设置证件类型, 借用表 ytbs3
            bo.setZjlx(ntp.getYtbs3());
            // 设置证件号码, 借用ytbs4
            bo.setZjhm(ntp.getYtbs4());
            // 设置投保单号,借用ytbs7
            bo.setTbdh(ntp.getYtbs7());
            // 设置货票人,使用kfhm
            bo.setKfhm(ntp.getKfhm());
            // 设置电子邮箱,使用ytbs8
            bo.setDzyx(ntp.getYtbs8());
            // 设置纳税人识别号, 使用的 用途标识9
            bo.setNsrsbh(ntp.getYtbs9());

            // 说明是投保单号查询 .
            if(CommonEnum.MERGE_QUERY_CONDITION_1.getKey().equals(dto.getMergeQueryCondition())){
                // 使用string的format格式, 传入 公共枚举的业务编码, 第二个参数 传入 交易机构,第三个参数 是使用Dateutils的格式化时间(第一个参数 当前时间,第二个参数是 同一个变量),第四个参数 一个随机六位数)
                // 投保单号的格式:交易机构(4位 1010)+ yyyyMMdd结构的年月日 + 一个6位随机数
                // ---- 这里是需求要求的 业务编号改成这个格式.
                bh = String.format(
                        CommonConstant.YWBH, // 告诉格式,使用%s的方式传入字符串
                        ntp.getJyjg(), // 4位
                        DateUtils.formatDate(new Date(),DateUtils.PURE_DATE_PATTERN), // Ji昂时间改成 年月日
                        RandomUtil.randomNumbers(6)
                ); // 第一个是字符串,后面都是传入的.
            }

//            设置业务编号, 使用编号
            bo.setYwbh(bh);
//            设置税率
            bo.setSysl(ntp.getSysl());
            // 获取当前日期
            LocalDate now = LocalDate.now();
            // 将日期转换为LocalDateTime，并将时分秒设为0(获取当天的午夜零点时间
            LocalDateTime localDateTime = now.atStartOfDay();
            // 并且将时间转换为 Date时间 - instant是java8的新属性能达到纳秒级别.
            Date date = Date.from(
                    localDateTime.atZone(
                            ZoneId.systemDefault()
                    ).toInstant()
            );
            //
            // 设置交易日期 为date
            bo.setJyrq(date);

            // 数组，储存Long值
            List<Long> lis = new ArrayList<>();
            // 数组里存储当前对象的rdid
            lis.add(ntp.getRdid());
//            将bo的rdid设置为新数组 rdids
            bo.setRdids(lis);

            // bhsje 不含税金额,jyje 交易金额,xxse 税额,wkpje 未开票金额 都是直接ntp取值。
            bo.setBhsje(ntp.getBhsje());
            bo.setJyje(ntp.getJyje());
            bo.setXxse(ntp.getXxse());
            bo.setWkpje(ntp.getWkpje());
             return bo;
        }
        //对金额进行累加
        // 只要是已经存在的对象,就会一直使用之前的数据,累加上去.  -- 所以根据某一个表合并,就是对他的金额进行累加. 投保单号进行修改 编号
        bo.setBhsje(bo.getBhsje().add(ntp.getBhsje()));
        bo.setJyje(bo.getJyje().add(ntp.getJyje()));
        bo.setXxse(bo.getXxse().add(ntp.getXxse()));
        bo.setWkpje(bo.getWkpje().add(ntp.getWkpje()));
        bo.getRdids().add(ntp.getRdid());

        // 获取人名称有值，并且俩不等。
        if (bo.getKfhm() != null && !bo.getKfhm().equals(ntp.getKfhm())) {
            throw new BusinessException(ResponseEnum.MERGING_TRANSACTION_FLOW_ERROR);
        }
        return bo;
    }
    /**
     * 验证合并选择的数据并且返回编号
     *
     * @param dto
     * @return
     */
    private String getBh(preMergeDTO dto) {
        // 下面是亮哥代码 ， 仔细看下。 然后写，并且提交。
        // 1.校验流水ID是否正确
        // 1.1 从新华 发票明细-新华（拓展） 里获取数据 -- 使用lambda 里获取单个对象。
        Long rdid = dto.getRdid();
        NvatTrandtadvalextXinhua xinhua = nvatTrandtadvalextXinhuaService.getNvatTrandtadvalextXinhuaById(rdid);
        NvatTrandtadvaltaxsep p = getNvatTrandtadvaltaxsepById(dto.getRdid(), true);
        if(p == null|| xinhua == null){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_DOES_NOT_EXIST);
        }


//        // 1.3 判断流水是否已拆分 或 已合并
        long count = nvatRevenuemapselfService.count(new LambdaQueryWrapper<NvatRevenuemapself>().eq(NvatRevenuemapself::getYrdid, dto.getRdid()));
        if(count> 0){
            throw new BusinessException(ResponseEnum.THE_DATA_DOES_NOT_MEET_THE_MERGING_REQUIREMENTS);
        }



        if (CommonEnum.MERGE_QUERY_CONDITION_0.getKey().equals(dto.getMergeQueryCondition())) { // 业务编号
            return p.getExpcol1(); // 相当于老表里的 拓展1,是业务编号.
        } else { // 投保编号
            return xinhua.getTbdh(); //
        }
    }

    public NvatTrandtadvaltaxsep getNvatTrandtadvaltaxsepById(Long id, boolean stateVerification) {
        if (ObjectUtil.isEmpty(id)) {
            return null;
        }
//        String getDeptCode = "1010";
        return getOne(new LambdaQueryWrapper<NvatTrandtadvaltaxsep>()
                .eq(NvatTrandtadvaltaxsep::getRdid, id)
                .eq(stateVerification, NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_1.getKey())
                .eq(stateVerification, NvatTrandtadvaltaxsep::getLszt, CommonEnum.FLOW_INVOICE_STATUS_1.getKey())
                .eq(NvatTrandtadvaltaxsep::getJyjg, deptCode)
        );
    }

    public Result<IPage<VmsMergeQueryVo>> mergeQuery(preMergeDTO dto) {
        String bh = getBh(dto); // 获取编号, 并且从三个表里(流水主表sep,拓展表xinhua,关系表self)里判断是否符合流程.
        Integer pageNo = dto.getPageNo();
        Integer pageSize = dto.getPageSize();
        Page<VmsMergeQueryVo> page = new Page<>(pageNo, pageSize);
        String ywbh = null;
        String tbdh = null;
        // 根据传过来的数据,判断是用业务编号,还是投保单号.
        if (CommonEnum.MERGE_QUERY_CONDITION_0.getKey().equals(dto.getMergeQueryCondition())) {
            ywbh = bh;
        } else {
            tbdh = bh;
        }
        // 部门统一使用北京分公司.
        String deptCode = "1010"; // SecurityUtils.getDeptCode()
        IPage<VmsMergeQueryVo> list = this.baseMapper.mergePageQuery(page, ywbh, tbdh, deptCode);
        return Result.OK(list);
    }

    @Transactional
    @Override
    public Result<Object> merge(preMergeDTO dto) {
            boolean flag = true;

            // 使用mergeRes,获取可以合并的列表. 返回list. 获取的列表是 按照税率或者险种合并后的列表. // 税率/险种合并后, 返回的列表.
            List<VmsMergeBo> list =     mergeRes(dto);

            // 日志记录列表长度
            log.info("{},合并后的长度,{}",LOG_MSG,list.size());
            // 如果不是空的数组 ,则进入判断
            if(CollectionUtil.isNotEmpty(list)){
                if(list.size()==1){ // 这里是判断表的长度是1, 就要判断 rdids是否是1
                    if(list.get(0).getRdids().size() == 1){
                        throw new BusinessException(ResponseEnum.MERGING_TRANSACTION_FLOW_ONE_ERROR);
                    }
                }

                // 循环 list
                for (VmsMergeBo bo : list) {
                    if(bo.getRdids().size() ==1){
                        // 如果 被循环的bo的rdids的长度是1,说明选中的rdids只有1个,就需要说流水需要两条以上才能合并有问题. MERGING_TRANSACTION_FLOW_ONE_ERROR
                        throw new BusinessException(ResponseEnum.MERGING_TRANSACTION_FLOW_ONE_ERROR);
                    }
                    // 记录 当前循环的循环的bo
                    log.info("{},合并后处理数据:{}",LOG_MSG,JSONUtil.toJsonStr(bo));
                    //1.查询到要处理的合并的数据 - 从sep里查询,限制条件 cfhbzt(拆分合并状态)1, 流水状态是1, 交易机构是 当前部门, 并且在rdids里的列表.(in) 组成tps
                    // - 从新查找表中,没拆封,流水状态 没处理的,当前部门. 并且是选中的rdids里的值 (突然懂了, 这里不查询rdid,是因为肯定没有这个id, 因为后面会把rdids里的值都会合并为一个值.
                    List<NvatTrandtadvaltaxsep> tps = this.list(new LambdaQueryWrapper<NvatTrandtadvaltaxsep>()
                            .eq(NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_1.getKey()) // 这里要是枚举有俩值,一定要写getkey. 而如果是常量,就可以不写(因为是静态值直接取值.
                            .eq(NvatTrandtadvaltaxsep::getLszt, CommonEnum.FLOW_INVOICE_STATUS_1.getKey())
                            .eq(NvatTrandtadvaltaxsep::getJyjg, deptCode)
                            .in(NvatTrandtadvaltaxsep::getRdid, bo.getRdids()));
                    // 如果当前的rdids的长度,和 tps的长度不一致,会报错 .  -- 合并的数据发现变更, 需要抛错\
                    if(bo.getRdids().size() != tps.size()){
                        //处理的数据和实际数据不一致
                        throw new BusinessException(ResponseEnum.MERGING_ERROR);
                    }



                    // 创建一个sep的实例
                    NvatTrandtadvaltaxsep npSep  = new NvatTrandtadvaltaxsep();
                    // 把获取的列表的第一个放到实例里
                    BeanUtils.copyProperties(tps.get(0),npSep); // 把属性粘贴过去 -- 这里其他属性 都在后面有重置, 所以直接粘贴第一个就行.
                    // 创建新华的实例, 从新华service里,根据id获取
                    NvatTrandtadvalextXinhua xinhua = nvatTrandtadvalextXinhuaService.getById(npSep.getRdid());
                    //  如果拓展表里没数据,就报错. 合并的数据中, 有数据拓展表中 找不到的数据,请联系管理员  TRANSACTION_FLOW_INFORMATION_EXTEND_NOT_EXIST
                    if(xinhua == null){
                        throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_EXTEND_NOT_EXIST);
                    }
                    //2.组装合并对象 ,传入当前对象bo, 主表np,新华拓展表
                    generateNewTransaction(bo,npSep,xinhua);
                    // 记录传入的 主表数据np,和新华表拓展表. {}是占位符,和参数一一对应. 第一个是log_msg,第二个是  使用JSONutil.toJsonStr(np),第三个是 toJsonStr的xinhua.
                    log.info("{},存储的数据,{}",LOG_MSG,JSONUtil.toJsonStr(npSep));
                    // 开始更新被合并的流水
                    // 只是更新一个字段, cfhbzt 为 合并(CommonEnum.SPLIT_MERGE_STATUS_3)
                    // ** 这里先更新主表1. 后面依次更新xinhua拓展表和self表
                    int update = this.baseMapper.update(null, new LambdaUpdateWrapper<NvatTrandtadvaltaxsep>()
                            .set(NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_3.getKey())
                            .in(NvatTrandtadvaltaxsep::getRdid, bo.getRdids())
                            .eq(NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_1.getKey())
                            .eq(NvatTrandtadvaltaxsep::getLszt, CommonEnum.FLOW_INVOICE_STATUS_1.getKey())
                            .eq(NvatTrandtadvaltaxsep::getJyjg, deptCode)
                    );
                    // 打印日志, {},{},log_msg , 合并后数据更新流水状态返回的值
                    log.info("{},发票合并后的对象,{}",LOG_MSG,update);
                    // 判断, 如果update更新返回后的数量,不等于当前bo的rdids的长度,就报错.  合并的数据状态发生变更 MERGING_ERROR
                    if(update !=bo.getRdids().size()){
                        throw new BusinessException(ResponseEnum.MERGING_ERROR);
                    }
                    //处理的数据和实际数据不一致

                    //3.保存合并后的流水
                    // 如果np的交易金额 如果是0, 就把np单条的数据 修改为 锁定.
                    if(npSep.getJyje().compareTo(BigDecimal.ZERO)== 0){
                        npSep.setLszt(Long.valueOf(CommonEnum.FLOW_INVOICE_STATUS_0.getKey()));
                    }else{
                        // else,  标识就改成  flag = false, 就说明 最终结果不是0
                        flag = false;
                    }
                    // 更新sep数据, 使用save,保存np ,如果成功了
                    // 这里保存了新的表,因为是合并后的新数据了.
                    if(this.save(npSep)){
                        // 日志保存的, np的 rdid
                        log.info("{},{}",LOG_MSG,JSONUtil.toJsonStr(npSep));
                        // 新华的对象, 也需要更新为 sep的.
                        // 更新新华的列表.
                        xinhua.setRdid(npSep.getRdid());
                        boolean save = nvatTrandtadvalextXinhuaService.save(  // 这里先更新更新xinhua拓展表 后面会更新self表
                                xinhua
                        );


                        //3.1 保存合并后的流水关联表

                        // 存一个空队列, 后期存放NvatRevenuemapself的队列.
                        List<NvatRevenuemapself> sefs = new ArrayList<>();
                        // 存一个空队列, 存放的是Long类型. 内容是 当前sep里的所有符合条件的id. (是传入的rdids, 的每个对象)
                        // 其实是 负数流水表的id集合
                        List<Long> furdids = new ArrayList<>(); // fu - 负 rdids
                        for (NvatTrandtadvaltaxsep p : tps) {
                            // 根据合并出来的数据, 需要再关系表里新增一条数据.
                            NvatRevenuemapself sef = new NvatRevenuemapself();

                            sef.setYrdid(p.getRdid());
                            sef.setXrdid(npSep.getRdid());
                            sef.setCzsj(new Date());
                            sef.setCzlx(CommonEnum.SPLIT_MERGE_TYPE_2.getKey());
                            sefs.add(sef);
                            if(p.getJyje().compareTo(BigDecimal.ZERO) == -1){
                                furdids.add(p.getRdid()); // 这里原本是sef的xrdid,实际不应该用这个.
                            }
                        }
                        nvatRevenuemapselfService.saveBatch(sefs); // 批量保存

                        //3.2 如果原数据是负数流水， 处理负数流水表；
                        // 判断原数据 负数长度的,如果有值,就需要在负数的表里更新对应的数据.
                        if(furdids.size()>0){
                            // 写lambda表达式, 更新的是负数流水表. // id应该是自增的, 在负数id的范围内-都需要更改-因为这里是出现了新的负数票.交易机构也不处理, 业务编号,原业务编号不需要修改.
                            boolean n = nvatNegativestreamForVatService.update(new LambdaUpdateWrapper<NvatNegativestream>()
                                    .set(NvatNegativestream::getClzt, CommonEnum.NEGATIVE_FLOW_STATUS_1.getKey()) // 设置状态为1, 已处理
                                    .set(NvatNegativestream::getCzr, username)//
                                    .set(NvatNegativestream::getCzrq, new Date())//
                                    .set(NvatNegativestream::getCllx, NegeClEnum.CLLX_04.getKey()) //
                                    .set(NvatNegativestream::getCzsm, NegeClEnum.CLLX_04.getValue()) //
                                    .in(NvatNegativestream::getRdid, furdids)
                                    .eq(NvatNegativestream::getClzt, CommonEnum.NEGATIVE_FLOW_STATUS_0.getKey()));
                            // 使用日志记录,  第一个是记录什么日志, 第二个是 更新了几个负数表, 第三个是 负数表的长度 -- 第二和第三个判断更新情况.
                            log.info("{},数据合并后,处理原数据对应的负数流水表.{},共: {}",LOG_MSG, n ,furdids.size());
                        }


                        //3.3 如果合并之后数据是负数流水， 添加到负数流水表；
                        // 判断 最终的np的交易金额,如果小于0 ,就要进入添加负数表的操作
                        if(npSep.getJyje().compareTo(BigDecimal.ZERO) == -1){

                            // 创建负数表的实力
                            NvatNegativestream nvatNegativestream = new NvatNegativestream();
                            // 负数表 设置rdid, 获取np的rdid
                            nvatNegativestream.setRdid(npSep.getRdid());
                            // 负数 设置业务编号 , 值是 sep的原交易流水 ysjyls
                            nvatNegativestream.setYwbh(npSep.getYsjyls());
                            // 负数 设置负业务编号, 值是getExpcol1
                            nvatNegativestream.setFuywbh(npSep.getExpcol1());
                            // 负数 设置投保人 是sep的khmc
                            nvatNegativestream.setTbr(npSep.getKfhm());
                            // 负数设置交易机构, sep jyjg
                            nvatNegativestream.setJyjg(npSep.getJyjg());
                            // 负数设置 识别日期, 是 当前日期
                            nvatNegativestream.setSbrq(new Date());
                            // 设置 处理状态, 为 负数-流水-状态-0  是 未处理
                            nvatNegativestream.setClzt(CommonEnum.NEGATIVE_FLOW_STATUS_0.getKey());
                            // 设置 负数流水管理 - 是否关注- 未关注
                            nvatNegativestream.setSfgz(CommonEnum.NEGATIVE_FLOW_MANAGE_0.getKey());
                            // 使用mp的save,直接保存对象
                            nvatNegativestreamForVatService.save(nvatNegativestream);
                            // 记录 {}-合并后数据合并后数据是负数，增加到负数流水表：{} ， -- 第二个值是 保存的数据流水
                            System.out.println(flag);
                            log.info("{},合并后处理 新的表如果是负数,并且存储",LOG_MSG,JSONUtil.toJsonStr(nvatNegativestream));
                            //  函数返回成功, 返回内容根据 标识 判断, 如果是 负数,就返回 --- "已合并，合并后开票金额为0，无需开票处理!"   "合并数据成功！"

                        }
                        return   Result.OK(flag ? "已合并,合并后开票金额为0,无需开票处理-- 前面校验了交易金额,确认累加后是0,就要走这个提示.":"合并数据成功---这里的提示就说明是 票据合并后,不是0");

                    }else{
                        System.out.println("没保存成功");
                        //  保存 sep的对象失败
                        throw new BusinessException(ResponseEnum.MERGING_ERROR);
                    }
                }

            }
            // 如果失败,就结果失败,并且响应值是 合并失败,合并结果无数据.
//            throw new BusinessException(ResponseEnum.MERGING_TRANSACTION_FLOW);
        return   Result.OK(flag ? "1111已合并,合并后开票金额为0,无需开票处理-- 前面校验了交易金额,确认累加后是0,就要走这个提示.":"2222合并数据成功---这里的提示就说明是 票据合并后,不是0");
//            return Result.OK("不知道逻辑问题错在哪里,正常来说应该是执行上面的报错. 原逻辑是 上面的return");
        }

    @Override
    public Result<VmsFlowInvoiceDetailVo> getMergeSplitInfo(String rdid) {
        // 第一步,获取 关系表的对象数据,
        NvatRevenuemapself entity = nvatRevenuemapselfService.getRevenuemapselftByXrdid(rdid);
        if(ObjectUtil.isNull(entity)){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_DOES_NOT_EXIST);
        }
        // 2.校验关系表,根据操作类型(合并/拆分)
        checkFlowInvoiceStatus(entity);

        // 3. 根据类型,展示不同的数据.
        VmsFlowInvoiceDetailVo invoiceDetailVo = new VmsFlowInvoiceDetailVo();
//        然后使用baseMapper 调用对应方法,传入相同的值.
            if(CommonEnum.SPLIT_MERGE_TYPE_1.getKey().equals(entity.getCzlx())){
                // 这里是拆分
                invoiceDetailVo = this.baseMapper.getSplitInfo(rdid);
            }else if(CommonEnum.SPLIT_MERGE_TYPE_2.getKey().equals(entity.getCzlx())){
                // 这里是合并
                invoiceDetailVo = this.baseMapper.getMergeInfo(rdid);
            }
        return Result.OK(invoiceDetailVo);
    }
    @Override
    public Result<IPage<VmsFlowInvoiceDetailVo>> getMergeSplitItem(preMergeDTO dto) {
        String rdid = String.valueOf(dto.getRdid());
        Integer pageNo = dto.getPageNo();
        Integer pageSize = dto.getPageSize();
        Page<VmsFlowInvoiceDetailVo> page = new Page<>(pageNo,pageSize);
        // 第一步,获取 关系表的对象数据,
        NvatRevenuemapself entity = nvatRevenuemapselfService.getRevenuemapselftByXrdid(rdid);
        if(ObjectUtil.isNull(entity)){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_DOES_NOT_EXIST);
        }

        // 3. 根据类型,展示不同的数据.
        IPage<VmsFlowInvoiceDetailVo> list = null;
//        然后使用baseMapper 调用对应方法,传入相同的值.
        if(CommonEnum.SPLIT_MERGE_TYPE_1.getKey().equals(entity.getCzlx())){
            // 这里是拆分
            list = this.baseMapper.getSplitItem(page,rdid);
        }else if(CommonEnum.SPLIT_MERGE_TYPE_2.getKey().equals(entity.getCzlx())){
            // 这里是合并
            list = this.baseMapper.getMergeItem(page,rdid);
        }
        return Result.OK(list);
    }

    @Override
    public Result<VmsManualInvoiceVo> getInvoiceInfoByTransactionFlow(List<Long> ids) {
        // *********这里的不懂的点
        // 1. checkStatus部分没看
        // 0.6 获取当前deptCode, 使用 ServletUtils.getHttpServletRequest().getAttribute("tokenUser");


        // 前期校验, 发票明细超过999,就报错.
        if(ids.size()> 999){
            throw new BusinessException(ResponseEnum.EXCESSIVE_INVOICE_FLOW);
        }
        // -- 原本的--校验投保单号下存在未处理负数流水
            // 使用checkPolicyNumber, 调用baseMapper 计算附属表里, 状态是没处理的,负数(值小于0)的总数. 判断是否>0,如果数量大于0 需要报错.
        Long count = baseMapper.checkPolicyNumber(ids);
        if(count > 0){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INVOICE_NEGATIVE_NUMBER_FLOW);
        }
        // checkStatus 判断状态,应该是 使用redis,获取自己的属性. 函数里会判断比如 1,2,3,4 在某个对象是错的, 其他的是对的.


        // 0. 根据id获取 流水, baseMapper, selectInvoiceByIds - 就是从主表sep+xinhua表里, 找到 in (ids)的值
        List<VmsTransacitonFlowInvoiceItemVo> itemVo = baseMapper.selectInvoiceByIds(ids);

        // 0.1 判断是否有负数流水, 是否是统一开票人, checkInvoiceRule
        checkInvoiceRule(itemVo,"");
                // 如果是专票,税率不能是0, 不然后
        if (itemVo.isEmpty()){
            throw new BusinessException(ResponseEnum.NO_FLOW_INFORMATION_FOUND);
        }
        // 0.5 构建最终返回对象
        VmsManualInvoiceVo voFinal = new VmsManualInvoiceVo();

        // 企业信息
        VmsEnterpriseInfo enterpriseInfo = vmsEnterpriseInfoService.getInfoByRedis(deptCode);
        System.out.println(enterpriseInfo);

        VmsTransacitonFlowInvoiceItemVo vmsTransacitonFlowInvoiceItemVo = itemVo.get(0);
        // 购买方名称
        String gmfmc = vmsTransacitonFlowInvoiceItemVo.getKfhm();
        // 购买方纳税人识别号 ------ 这个俩取那个表里的都可以吗
        String gmfnsrsbh = vmsTransacitonFlowInvoiceItemVo.getNsrsbh();
        // 开票方名称 ------- itemVo里也有
        String kpfmc = enterpriseInfo.getNsrmc();
        // 开票方纳税人识别号    --------  itemVo里也有
        String kpfnsrsbh = enterpriseInfo.getNsrsbh();
        // 开票人
        String kpr = username;

        // 1. 获取发票base信息
        VmsManualBaseVo vmsManualBaseVo = new VmsManualBaseVo();
        // 1.1 先获取开票方信息(得到开票方名称,纳税人识别号,开票人,购买方名称,纳税人识别号)

        vmsManualBaseVo.setGmfmc(gmfmc);
        vmsManualBaseVo.setGmfnsrsbh(gmfnsrsbh);
        vmsManualBaseVo.setXsfmc(kpfmc);
        vmsManualBaseVo.setXsfnsrsbh(kpfnsrsbh);
        vmsManualBaseVo.setKpr(kpr);

        voFinal.setVmsManualBaseVo(vmsManualBaseVo);
        // 2. 获取发票明细信息,
        List<VmsManualDetailVo>  detailList =     getVmsManualDetailVos(itemVo,vmsManualBaseVo);
        voFinal.setVmsManualDetailVoList(detailList);

            // 调用 getVmsManualDetailVos方法, 返回值就直接设置了
        // 3. 获取发票拓展信息
        VmsManualExtendVo extendVo = new VmsManualExtendVo();
        extendVo.setXsfdz(enterpriseInfo.getScjydz()); // 销售方地址 -> 生产经营地址
        extendVo.setXsfdh(enterpriseInfo.getZcdlxdh()); // 销售方电话 -> 注册地联系电话
        extendVo.setXsfzh(enterpriseInfo.getYhzh()); // 销售方账号 -> 银行账号
        extendVo.setXsfkhh(enterpriseInfo.getYhyywddm()); // 销方开户行 -> 银行业务网点

        extendVo.setSkyhmc(enterpriseInfo.getKhhmc());
        extendVo.setSkyhzh(enterpriseInfo.getYhzh());
        extendVo.setQydm("区域代码-测试"); // 区域代码 -需要从板式文件里获取,没写哪块.
        extendVo.setYsxwfsd("区域名称-测试"); // 应税行为发生地 ->   区域名称  -需要从板式文件里获取,没写哪块.
        voFinal.setVmsManualExtendVo(extendVo);
        return Result.OK(voFinal);
    }

    private List<VmsManualDetailVo> getVmsManualDetailVos(List<VmsTransacitonFlowInvoiceItemVo> itemVo, VmsManualBaseVo vmsManualBaseVo) {
        // 创建一个列表,并且返回的  发票明细列表  , 类型是detail的vo.
        ArrayList<VmsManualDetailVo> resList = new ArrayList<>();
        // 应该是去重并且累加用的, HashMap,每次key值相同就取相同的value. / 一个对象,key是字符串, 值是数据库VMS_EPS_INFORMATION的对象.
        HashMap<String, VmsEpsInformation> epsMap = new HashMap<>();
        // 明细序号 ,后续每次使用会自动累加  mxxh
        int mxxh = 1;
        // 定义合计金额 Big的0   hjje
        BigDecimal hjje = BigDecimal.ZERO;
        // 定义合计税额   hjse
        BigDecimal hjse = BigDecimal.ZERO;
        // 定义合计交易金额 jyje
        BigDecimal jyje = BigDecimal.ZERO;
        // 列表进行循环, for循环itemVo
        for (VmsTransacitonFlowInvoiceItemVo item : itemVo) {
//            从单个item中获取税率,并且scale(4) 保留4位
            BigDecimal sl4 = item.getSysl().setScale(4);
            // 根据税率,获取对应的eps信息.(企业商品及服务信息管理表)
            VmsEpsInformation epsInformation = epsMap.get(sl4);
            // 如果获取的 eps企业信息数据是空的,
            if(ObjectUtil.isEmpty(epsInformation)){
                // 就会添加到hashmap里( 税率, 使用service调用获取数据.)
                VmsEpsInformation infoBySlv = vmsEpsInformationService.getInfoBySlv(sl4.toPlainString(),deptCode);
                // 从eps企业信息中获取数据, 重置epsinformation
                epsInformation = infoBySlv;
            }

            // 新建一个详情vo VmsManualDetailVo
            VmsManualDetailVo vmsManualDetailVo = new VmsManualDetailVo();

            // 设置主键id, 是item的id, 设置detail的主键id是 发票匹配流水的rdid
            vmsManualDetailVo.setId(item.getId());
            // 再次新建一个列表, 里面的值是字符串.
            ArrayList<String> list = new ArrayList<>();
            // 判断 - 如果epsInformation 获取请求后,还是空的,就抛错. 说产品信息是空的.
            if(ObjectUtil.isEmpty(epsInformation)){
                throw new BusinessException("222","随便写的报错信息"); // 随便写的报错信息
//                throw new BusinessException(ResponseEnum.NO_FLOW_INFORMATION_FOUND); // 随便写的报错信息
            }
            // 获取商品分类及名称的简称? -- 提示的
            // 获取商品名称
            // 拼接: * + 商品服务简称+ * + 险种简称
            // 拼接2: * + 商品服务简称+ * 商品服务名称
            String sphfwfljc = epsInformation.getSphfwfljc();
            String epsName = epsInformation.getEpsName();
            // 设置项目名称 是 list的第一项 ** 这个很奇怪,是添加了俩值,为啥只是使用了第一个
            list.add("*" + sphfwfljc + "*" + item.getXzjc());
            list.add("*" + sphfwfljc + "*" + epsName);

            // 自动累加的明细序号
            vmsManualDetailVo.setXmmc(list.get(0));
            vmsManualDetailVo.setMxxh((long) mxxh);
            // 多个项目名称 , 这个debugger一下,看看 是不是list里俩字符串 xmmclist 追加list
            vmsManualDetailVo.setXmmcList(list);
            // 设置规格型号, 是item的expcol1
            vmsManualDetailVo.setGgxh(item.getExpcol1());
            // 设置单位, 是计量单位
            vmsManualDetailVo.setDw(item.getJldw());
            // 判断  项目数量如果是空的, 那就设置为"1", 其他情况就是把原本的xmsl设置进去.
            if(ObjectUtil.isEmpty(item.getXmsl())){
                vmsManualDetailVo.setSl("1");
            }else {
                vmsManualDetailVo.setSl(item.getXmsl().toPlainString());
            }
            // 得到一个单价 dj =  把原本的不含税金额, 除以税率,保证25位, HALF_UP的格式 四舍五入
            String dj = formatBigDecimalToString(item.getBhsje().divide(new BigDecimal(vmsManualDetailVo.getSl()), 25, RoundingMode.HALF_UP));
            vmsManualDetailVo.setDj(dj);
            // 判断 item如果项目单价不是空的
            if(ObjectUtil.isNotEmpty(item.getXmdj())){
                // 设置含税单价 为xmdj,并且转成字符串 getPlainString
                vmsManualDetailVo.setHsdj(item.getXmdj().toPlainString());
            }
            // detail设置金额, 是item的不含税金额
            vmsManualDetailVo.setJe(item.getBhsje());
            // 设置税率, 是item税率
            vmsManualDetailVo.setSl(item.getSysl().toPlainString());
            // 设置税额,是xxse
            vmsManualDetailVo.setSe(item.getXxse());
//            hjje = hjje + bhsje
            jyje = jyje.add(item.getJyje());
            // 税额是税额
            // 交易金额 是交易金额
//            最终的list 添加一个当前对象
            resList.add(vmsManualDetailVo);
        }
        // 外层的vo , 设置价税合计 为交易金额
        vmsManualBaseVo.setJshj(jyje);
        // 返回列表
        return  resList;
    }
    /**
     * 将BigDecimal转为String
     * String的最大长度为25
     */
    private String formatBigDecimalToString(BigDecimal bigDecimal) {
        if (ObjectUtil.isEmpty(bigDecimal)) {
            return "";
        }
        // 单价、数量先保留6位小数
        String formatResult = bigDecimal.setScale(6, RoundingMode.HALF_UP).stripTrailingZeros().toPlainString();
        if (formatResult.length() > 25) {
            formatResult = formatResult.substring(0, 25);
        }
        return formatResult;
    }
    private void checkInvoiceRule(List<VmsTransacitonFlowInvoiceItemVo> itemVo, String fppz) {
        int size = itemVo.size();
        VmsTransacitonFlowInvoiceItemVo vmsTransacitonFlowInvoiceItemVo1 = itemVo.get(0);
        String kfhm = vmsTransacitonFlowInvoiceItemVo1.getKfhm();
        for (int i = 0; i < size; i++) {
            VmsTransacitonFlowInvoiceItemVo vmsTransacitonFlowInvoiceItemVo = itemVo.get(i);
            // 交易金额不能小于0, 如果小于0 就是 无权限,需要提示
            if(vmsTransacitonFlowInvoiceItemVo.getBhsje().compareTo(BigDecimal.ZERO) < 0){
                throw new BusinessException(ResponseEnum.VERIFICATION_INVOICING_ROLES_IN_FLOW_CHART_02);
            }
            // 不能是同一个货票人
            if(!vmsTransacitonFlowInvoiceItemVo.getKfhm().equals(kfhm)){
                throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INVOICE_TICKET_RECIPIENT);
            }
            // 专票不能用0税率
            if(vmsTransacitonFlowInvoiceItemVo.getSysl().compareTo(BigDecimal.ZERO) == 0 &&  "01".equals(fppz)){
                throw new BusinessException(ResponseEnum.BUSINESS_XSF_GMFNSRSBH_ERROR);
            }
        }

    }


    // 拆分流水
    @Override
    public  Result<Object> splitTransactionFlow(VmsFlowSplitDto dto){
        Long rdid = dto.getId();
        List<VmsFlowSplitDto.VmsFlowSplitItem> list = dto.getItem();
        // 1. 获取数据
        NvatTrandtadvaltaxsep sep = getNvatTrandtadvaltaxsepById(rdid, true);
        NvatTrandtadvalextXinhua xinhua = nvatTrandtadvalextXinhuaService.getById(rdid);
        if(sep == null || xinhua == null){
            throw new BusinessException(ResponseEnum.TRANSACTION_FLOW_INFORMATION_DOES_NOT_EXIST);
        }
        // 负数流水标识
        boolean fslsFlag = sep.getJyje().compareTo(BigDecimal.ZERO) < 0;

        // 校验
        verification(list);

        // 更新原流水
        boolean update = this.update(new LambdaUpdateWrapper<NvatTrandtadvaltaxsep>()
                .eq(NvatTrandtadvaltaxsep::getRdid, rdid)
                .set(NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_2.getKey())
                .eq(NvatTrandtadvaltaxsep::getCfhbzt, CommonEnum.SPLIT_MERGE_STATUS_1.getKey())
                .eq(NvatTrandtadvaltaxsep::getLszt,CommonEnum.FLOW_INVOICE_STATUS_1.getKey()) // 没开票的 - 需要校验
        );
        if(!update) {
            throw new BusinessException(ResponseEnum.MERGING_ERROR);
        }
//        这里还需要更新 负数流水



        // 拆分新流水
        for (int i = 0; i < dto.getItem().size(); i++) {
            VmsFlowSplitDto.VmsFlowSplitItem vmsFlowSplitItem = dto.getItem().get(i);
            NvatTrandtadvaltaxsep NvatTrandtadvaltaxsep1 = new NvatTrandtadvaltaxsep();
            BeanUtils.copyBeanProp(NvatTrandtadvaltaxsep1,sep);
            NvatTrandtadvaltaxsep1.setRdid(null);
            NvatTrandtadvaltaxsep1.setJyje(vmsFlowSplitItem.getHsje());

            // 下面这六行,是自己写完之后没写的内容.
            NvatTrandtadvaltaxsep1.setJyrmbje(vmsFlowSplitItem.getHsje());
            NvatTrandtadvaltaxsep1.setBhsje(vmsFlowSplitItem.getBhsje());
            NvatTrandtadvaltaxsep1.setXxse(vmsFlowSplitItem.getSe());
            NvatTrandtadvaltaxsep1.setWkpje(vmsFlowSplitItem.getHsje());
            NvatTrandtadvaltaxsep1.setCfhbzt(Long.valueOf(CommonEnum.SPLIT_MERGE_TYPE_1.getKey()));
            NvatTrandtadvaltaxsep1.setLszt(Long.valueOf(CommonEnum.FLOW_INVOICE_STATUS_1.getKey()));

            // 原本是update, 实际项目中是save. 应该是 新建,而不是原本的更新值.
            boolean save2 = save(
                    NvatTrandtadvaltaxsep1
            );
            System.out.println(save2);

            Long xrdid = NvatTrandtadvaltaxsep1.getRdid();

            NvatTrandtadvalextXinhua nvatTrandtadvalextXinhua2 = new NvatTrandtadvalextXinhua();
            BeanUtils.copyProperties(xinhua,nvatTrandtadvalextXinhua2);
            nvatTrandtadvalextXinhua2.setRdid(xrdid); // 这里的rdid不能是空不能是null,必须是上层sep传递过来的值.   如果按照null,随机生成就不对,后续无法继续找到.
            nvatTrandtadvalextXinhua2.setXmsl(BigDecimal.ONE); // 明细行的 数量,默认是1.
            boolean save = nvatTrandtadvalextXinhuaService.save(nvatTrandtadvalextXinhua2);
            if(!save){
                throw new BusinessException(ResponseEnum.MERGING_ERROR);
            }


            NvatRevenuemapself nvatRevenuemapself3 = new NvatRevenuemapself();
            nvatRevenuemapself3.setYrdid(rdid);
            nvatRevenuemapself3.setXrdid(xrdid);
            nvatRevenuemapself3.setCzlx(CommonEnum.SPLIT_MERGE_TYPE_1.getKey()); // 操作类型 1拆分 2合并
            nvatRevenuemapself3.setCzsj(new Date());
            boolean save1 = nvatRevenuemapselfService.save(nvatRevenuemapself3);
            if(!save1){
                throw new BusinessException(ResponseEnum.MERGING_ERROR);
            }

        }


        return  Result.OK("假数据-拆分成功.");
    }

    @Override
    public Result<Object> invoiceByTransactionFlow(VmsTransactionFlowInvoiceDto dto) { // 111
//        已开发票 - 主体信息
//        已开发票 - 队列,用来提交 到已开发票里的
//        创建单个已开发票队列
//        创建单个已开发票信息
//        获取销售方信息
//        根据流水信息累计的合计金额/合计税额/交易金额
        VmsManualBaseDto vmsManualBaseDto = dto.getVmsManualBaseDto();
        List<VmsManualDetailDto> vmsManualDetailDto = dto.getVmsManualDetailDto();
        VmsManualExtendDto vmsManualExtendDto = dto.getVmsManualExtendDto();
        ArrayList<VmsFullOutInvoiceInfoDto> vmsFullOutInvoiceInfoDtos = new ArrayList<>();
        VmsFullOutInvoiceInfoDto vmsFullOutInvoiceInfoDto = new VmsFullOutInvoiceInfoDto();

        VmsEnterpriseInfo enterpriseInfo = vmsEnterpriseInfoService.getInfoByRedis(deptCode);
        BigDecimal sumhjje = BigDecimal.ZERO;
        BigDecimal sumhjse = BigDecimal.ZERO;
        BigDecimal sumjyje = BigDecimal.ZERO;
        VmsFullOutInvoiceDetails detail = new VmsFullOutInvoiceDetails();


        detail.setLzfpbz(LzfpbzEnum.BULE.key);
        detail.setFppz(vmsManualBaseDto.getFppz());
        // 这些字段都没设置
        //        detail.setFphm(); // 不需要发票号码是因为 后续会调用旭东的一个接口,那边设置了.
//        detail.setFoidid(); // 应该也是旭东那边处理了
//        detail.setPtbh(); // 平台编号, 也没处理
//            detail.setDhlx(vmsManualBaseDto.getDhlx()); -------为啥没有? 看旭东接口-----------
//            detail.setDh(vmsManualBaseDto.getDh()); ---- 为啥没有? 看旭东接口-----------
//            detail.setTdys(vmsManualBaseDto.getTdys());  -----为啥没有? 看旭东接口-----------
//            detail.setCezslxdm(vmsManualBaseDto.getCezslxdm());  -----为啥没有? 看旭东接口-----------
        //        detail.setJems(vmsManualBaseDto.getJems()); // 金额模式, 01 不含税, 02含税  ***********使用的是公共枚举的,key值. ********* 直接设置定为不含税,是为什么?
        //            detail.setDylzfphm(vmsManualBaseDto.s); // 一会需要看下- 对应蓝字发票,是不是后续旭东的接口
//            detail.setHzqrxxdbh(); // 红字确认信息单编号 是否蓝字发票,标志是1的时候,这个值有内容. 红票开具时,必传
//            detail.setHzqrduuid(); // 红字确认信息单uuid 是否蓝字发票,标志是1的时候,这个值有内容. 红票开具时,必传
        //            detail.setSllsh(); // 受理流水号, 乐其数电发票上传返回的受理流水号
//            detail.setScjg(); // 上传结果, 肯定是 旭东后续调用乐企的值
//            detail.setZfchrq(); // 作废冲红日期
//            detail.setZfchyy(); // 作废冲红原因
        //            detail.setDelFlag() // 删除状态
//            detail.setBaseId(); // 手工开票 临时表主表id
//            detail.setOidid(); // vms发票表主键, 用于关联老发票表
//        detail.setSjkly(); // 数据库来源: 1 进项票 2 税控票 3 数电票
        detail.setJems(CommonEnum.AMOUNT_MODE_01.getKey());
        detail.setGmfmc(vmsManualBaseDto.getGmfmc());
        detail.setGmfnsrsbh(vmsManualBaseDto.getGmfnsrsbh());

        detail.setXsfmc(vmsManualBaseDto.getXsfmc()); // 销售方默认是当前 公司 ***************
        detail.setXsfnsrsbh(vmsManualBaseDto.getXsfnsrsbh());//  销售方识别号默认是当前 公司 *************
        detail.setKpr(username); // ***********使用的securityUtils ,也就是当前登录人**************
        detail.setKprq(new Date());
        detail.setBz(vmsManualBaseDto.getBz());

        detail.setBz(vmsManualBaseDto.getBz());
        detail.setSczt(CommonEnum.INVOICE_UPLOAD_STATUS_0.getKey()); // 上传状态, 0是未上传.
        detail.setFpzt(FpztEnum.NORMAL.getKey()); // 发票状态, 0 是正常. 可能是使用常量
        detail.setQydjid(deptCode); // --------交易机构 字段

        detail.setSjly(CommonEnum.DATA_SOURCE_0.getKey());// 数据来源
        detail.setPpzt(CommonEnum.MATCHING_STATUS_1.getKey());// 匹配状态;0：未完全匹配，1：已完全匹配，-1：无需匹配，且只针对手工开票数据
        detail.setSfbdc(CommonEnum.SFBDC_NO.getKey()); // 是否不动产;0：不是，1：是，默认0
        detail.setZsfs(CommonEnum.TAXATION_METHOD_0.getKey()); // 征税方式;0：普通征税，2：差额征税
        detail.setKpzt(CommonEnum.KPZT_0.getKey()); // 开票状态;0：未开票 2：开票成功 3：开票失败

        detail.setCreatTime(new Date());
        detail.setCreatBy(username);
        detail.setUpdateTime(new Date()); // 更新时间,应该不用处理. 更新的时候再说
        detail.setUpdateBy(username); // 更新人,应该不用处理,更新的时候再说



        List<VmsFullOutInvoiceItems> items = new ArrayList<>();
        ArrayList<Long> lsIds = new ArrayList<>(vmsManualDetailDto.size());
        HashMap<Long, String> objectObjectHashMap = new HashMap<>();
        for (int i = 0; i < vmsManualDetailDto.size(); i++) {
            VmsManualDetailDto detailItem = vmsManualDetailDto.get(i);
            Long id = detailItem.getId();
            lsIds.add(id);
            String xmmc = detailItem.getXmmc();
            objectObjectHashMap.put(detailItem.getId(),xmmc);
        }


        // 第二个循环
        List<VmsTransacitonFlowInvoiceItemVo> itemVo = baseMapper.selectInvoiceByIds(lsIds);
        HashMap<String,VmsEpsInformation> slMap = new HashMap<>();
        for (int i = 0; i < itemVo.size(); i++) {
            VmsFullOutInvoiceItems item = new VmsFullOutInvoiceItems();
            VmsTransacitonFlowInvoiceItemVo vmsTransacitonFlowInvoiceItemVo = itemVo.get(i);
            item.setMxxh(Long.valueOf(i));
//            item.setDylzfpmxxh(); // 没设置, 后续在处理.
            item.setXmmc(vmsTransacitonFlowInvoiceItemVo.getKfhm());
            item.setGgxh(vmsTransacitonFlowInvoiceItemVo.getExpcol1());
            item.setDw(vmsTransacitonFlowInvoiceItemVo.getJldw());
            if(ObjectUtil.isNotEmpty(vmsTransacitonFlowInvoiceItemVo.getXmsl())){
                item.setSl(vmsTransacitonFlowInvoiceItemVo.getXmsl().toPlainString());
            }
            if(ObjectUtil.isNotEmpty(vmsTransacitonFlowInvoiceItemVo.getXmdj())){
                item.setDj(vmsTransacitonFlowInvoiceItemVo.getXmdj().toPlainString());
            }
//            item.setHsdj(); // 没设置
            item.setJe(vmsTransacitonFlowInvoiceItemVo.getBhsje());
            item.setSlv(vmsTransacitonFlowInvoiceItemVo.getSysl());
            item.setSe(vmsTransacitonFlowInvoiceItemVo.getXxse());
//            item.setKce(vmsTransacitonFlowInvoiceItemVo.get); // 特定情况下会设置, 初始化对象不用设置
            BigDecimal String4 = vmsTransacitonFlowInvoiceItemVo.getSysl();
            VmsEpsInformation eps = slMap.getOrDefault(String4.toString(), vmsEpsInformationService.getInfoBySlv(String4.toPlainString(), deptCode));
            item.setSphfwssflhbbm(eps.getGstcmCode());
            item.setFphxz(CommonEnum.FPHXZ_00.getKey());
            item.setCpbm(eps.getEpsCode()); // 企业商品及服务信息管理表

            if(StringUtils.isNotEmpty(eps.getJsff())){
                item.setJsff(eps.getJsff());
            }
            if(StringUtils.isNotEmpty(eps.getZsxm())){
                item.setZsxm(eps.getZsxm());
            }
            if(StringUtils.isNotEmpty(eps.getYhzcbs())){
                item.setYhzcbs(eps.getYhzcbs());
            }
//            foidid/oidid/iviid
            item.setCreatTime(new Date());
            item.setCreatBy(username);
            items.add(item);

            sumhjje = sumhjje.add(item.getJe());
            sumhjse = sumhjse.add(item.getSe());
            sumjyje = sumjyje.add(vmsTransacitonFlowInvoiceItemVo.getJyje());
            item.setRdid(String.valueOf(vmsTransacitonFlowInvoiceItemVo.getId()));


        }
        // 将base行 基础信息设置
        detail.setHjje(sumhjje);
        detail.setHjse(sumhjse);
        detail.setJshj(sumjyje);
        vmsFullOutInvoiceInfoDto.setVmsFullOutInvoiceDetails(detail);
        // 将明细行设置
        vmsFullOutInvoiceInfoDto.setVmsFullOutInvoiceItemsList(items);
        // 设置拓展信息
        if(ObjectUtil.isNotEmpty(vmsManualExtendDto)){
            VmsFullOutInvoiceItemsExp vmsFullOutInvoiceItemsExp = new VmsFullOutInvoiceItemsExp();
            BeanUtils.copyBeanProp(vmsFullOutInvoiceItemsExp,vmsManualExtendDto);
            vmsFullOutInvoiceInfoDto.setVmsFullOutInvoiceItemsExp(vmsFullOutInvoiceItemsExp);
        }

        vmsFullOutInvoiceInfoDtos.add(vmsFullOutInvoiceInfoDto); // 因为这里是最终传递给已开票接口,可能那边只是接受列表
        // 开始循环修改ids,这样把这些状态都改成已开票

        for (Long lsId : lsIds) {
            LambdaUpdateWrapper<NvatTrandtadvaltaxsep> sep = new LambdaUpdateWrapper<>();// 全部开票

            sep.set(NvatTrandtadvaltaxsep::getLszt,3);
            sep.set(NvatTrandtadvaltaxsep::getWkpje,BigDecimal.ZERO);
            sep.eq(NvatTrandtadvaltaxsep::getRdid,lsId);
            sep.eq(NvatTrandtadvaltaxsep::getLszt,1);
            boolean update = update(sep);
            if(!update){
                throw new BusinessException(ResponseEnum.FLOW_STATUS_UPDATE_FAIL);
            }
        }













// 后续修改
//        detail.setHjje();
//        detail.setHjse();
//        detail.setJshj();

//        for (int i = 0; i < vmsManualDetailDto.size(); i++) {
//            VmsManualDetailDto detail = vmsManualDetailDto.get(i);
//        }
        /*
    * 总逻辑:   修改原流水的所有状态, 并且创建已开发票的数据.
    * 细节逻辑:
    * 1. 获取流水明细的id, 就是detail的
    * 2.获取发票类型
    * 3.根据id查询流水, 使用baseMapper, 返回一个list.
    * 4. 校验发票开票规则
    * 5. 调用manualInvoice方法
    *   5.1 获取base对象
    *   5.2 获取明细
    *   5.3 获取拓展
    *   创建arraylist, dtos - 用来存放已开发票列表s
    *   创建一个已开发票对象 VmsFullOutInvoiceInfoDto
    *   准备销售方信息, 从redis里获取 vmsEnterpriseInfoService.getInfoByRedis(传入部门)
        *   准备一个合计金额,合计税额,交易金额.
        *   准备一个 已开发票的 发票主题信息
        *   设置23个状态  有43个状态.
    * 开始设置明细
    *   创建一个发票明细的列表
    *   创建一个储存流水id的集合
    *   创建一个hashMap, 用来存储项目名称是value 的map
    *   循环,最终存储一个key是发票明细的id.  value是xmmc
    *   循环结束, 使用baseMapper 获取id的itemVo(也就是发票流水明细的详情信息)
    *   再次开始循环, itemVo的长度
    *   创建已开发票的对象.
    *   设置项目名称
    *   设置明细行
    *   设置规格型号
    *   设置单位
    *   判断发票的项目数量, 如果不是空(说明有值)
    *       设置数量, 从获取对象的vo里设置.
    *   判断是否有单价,有单价设置单价.
    *   设置金额
    *   设置税率
    *   税率 保留4位
    *   hash里 测试,如果有这个税率保留4位,没有就设置,有就获取eps
    *   判断下 如果eps是空的,就报错.
    *   设置税额
    *   设置扣除额
    *   设置fphxz 发票行性质
    *   设置cpbm
    *   设置创建时间
    *   设置创建人
    *   如果 商品和服务编码 存在,设置 商品和服务税收分类合并编码
    *   如果 计税方法 存在, 重置 计税方法
    *   如果 征税项目存在,重置 征税项目
    *   如果优惠政策标识 存在, 重置优惠政策标识
    *   循环结束,累加 金额,税额,交易金额
    *   items添加item
    *   重置rdid为 当前想循环出来的值.
    *   base设置合计金额,合计税额, 价税合计.
    *
    *   开始新建已开发票部分.
    *       判断是否有拓展信息
    *           有就重置 发票的拓展信息,使用Bean的copy部分直接粘贴过去
    *   将整体的已开发票dto,add到dtos里.
    *
    *
    *   修改流水状态
    *   循环LsIds
    *       使用lambda修改 原流水的状态. 未开票金额 改成0, rdid是当前id, 原来的lszt是1
    *       走update
*       
    *

     * */
        System.out.println(dto);
        return null;
    }

    private static boolean verification(List< VmsFlowSplitDto.VmsFlowSplitItem> list) {
        BigDecimal hsje = new BigDecimal("0");
        BigDecimal bhsje = new BigDecimal("0");
        BigDecimal se = new BigDecimal("0");


        for (VmsFlowSplitDto.VmsFlowSplitItem invoiceDetailVo : list) {

            BigDecimal hsje1 = invoiceDetailVo.getHsje();
            BigDecimal bhsje1 = invoiceDetailVo.getBhsje();
            BigDecimal se1 = invoiceDetailVo.getSe();
            hsje1.abs().add(hsje);
            bhsje1.add(bhsje);
            se1.add(se);
        }
        // 2. 比较大小,出问题就抛错.
        // 先获取原数据
        // 这里可以封装一个函数, 获取原数据.
        // 是否负数流水,
        // 更新原sep,self,xinhua
        // 根据list的个数,新建多个. 新表.
//        this.getOne(
//                new LambdaQueryWrapper<NvatTrandtadvaltaxsep>()
//                        .eq(NvatTrandtadvaltaxsep::getRdid,rdid)
//        )
        return true;
    }

    /**
     * 生成新流水信息
     *
     * @param bo
     * @param np
     */

    private static void generateNewTransaction(VmsMergeBo bo, NvatTrandtadvaltaxsep np, NvatTrandtadvalextXinhua xinhua) {
        //1.主表数据
        BeanUtils.copyProperties(bo, np);
        np.setRdid(null); // 这里直接置空, 是因为前面是复制过来的. -- 这个rdid没有用.
        //交易人民币金额
        np.setJyrmbje(bo.getJyje());
        //负数流水和正数流水处理
        if (bo.getJyje().compareTo(BigDecimal.ZERO) == -1) {
            np.setLsxz(Long.parseLong(CommonEnum.LSXZ_2.getKey()));
            np.setYsjyls(CommonConstant.YYWBH + bo.getYwbh());
        } else {
            np.setLsxz(Long.parseLong(CommonEnum.LSXZ_1.getKey()));
            np.setYsjyls("");
        }

        //未拆分合并
        np.setCfhbzt(Long.valueOf(CommonEnum.SPLIT_MERGE_STATUS_1.getKey()));
        //未开票
        np.setLszt(Long.parseLong(CommonEnum.FLOW_INVOICE_STATUS_1.getKey()));
        //供数日期当前时间
        np.setGsrq(new Date());
        //业务编号
        np.setExpcol1(bo.getYwbh()); // 业务编号
        np.setExpcol3(bo.getXzdm()); // 险种代码
        np.setExpcol4(bo.getXzjc()); // 险种简称
        np.setKhmc(bo.getKfhm());
        np.setKhsjh(bo.getSjh()); // 客户手机号
        np.setKhemail(bo.getDzyx()); // 电子邮箱
        //2.扩展表数据
        BeanUtils.copyProperties(bo, xinhua);
        xinhua.setBqxmdm(""); // 保全项目代码
        xinhua.setBqxmmc(""); // 保全项目名称
        xinhua.setJfjzrq(null); // 缴费止期
        xinhua.setJfqsrq(null); // 缴费起期
        xinhua.setFwrybh(""); // 服务人员编号
        xinhua.setFwryxm(""); // 服务人员姓名
        xinhua.setQmc(""); // 区
        xinhua.setBmc(""); // 部
        xinhua.setZmc(""); // 组
        xinhua.setCpdm(bo.getXzdm());  // 产品代码
        xinhua.setCpmc(bo.getXzjc()); // 产品名称
        xinhua.setBz(""); // 备注
        xinhua.setGgxh(bo.getXzdm()); // 规格型号
        xinhua.setXmdj(bo.getBhsje()); // 单价  - 不含税金额
        xinhua.setTbrxm(bo.getKfhm()); // 投保人姓名
        xinhua.setXmsl(BigDecimal.ONE); // 项目数量
        xinhua.setHjdh(""); // 签单后的汇交单号
        xinhua.setXpxslh(""); // 学平险受理号
        xinhua.setYwlx(CommonConstant.DBDHB_YWLX); // 业务类型：1.新契约,2.保全,3.续期,4.理赔,5.续保,9.其他
        xinhua.setSysbm(""); // 税优识别码
        xinhua.setJffs(""); // 缴费方式: 1：月缴 ; 12：年缴
    }
}